package cn.dengta.webapp.user.entity.task;

import java.math.BigDecimal;
import java.util.Date;
import javax.annotation.Nullable;

import cn.dengta.common.context.I18N;
import cn.dengta.common.model.*;
import cn.dengta.context.model.*;
import cn.dengta.webapp.base.entity.BizEntity;
import cn.dengta.webapp.base.entity.I18nContent;
import cn.dengta.webapp.user.entity.User;
import cn.dengta.webapp.user.model.Gender;
import cn.dengta.webapp.user.model.TaskType;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.codeplayer.util.*;

import static cn.dengta.context.model.UserEnumI18nKey.*;

/**
 * 任务规则表
 *
 * @author lgy
 * @since 2022-03-05
 */
@Setter
@Getter
@Accessors(chain = true)
@TableName("dt_task_rule")
public class TaskRule extends BizEntity implements SwitchSupport {

	/** 用户ID */
	private Long userId;
	/** 名称 */
	private String name;
	/** 图标 */
	private String icon;
	/** 任务类型 */
	private TaskType type;
	/** 业务类型 */
	private TaskRuleType ruleType;
	/** 最小钻石 */
	private Integer minCoin;
	/** 最大钻石（范围值时候设置，否则为null） */
	private Integer maxCoin;
	/** 领取最大次数 */
	private Integer maxLimit;
	/** 开始时间 */
	private Date beginTime;
	/** 结束时间 */
	private Date endTime;
	/** 规则JSON配置 */
	private String config;
	/** 概率规则JSON配置 */
	private String factorConfig;
	/** 备注信息 */
	private String remark;
	/** 排序 */
	private Integer sort;
	/** 奖励类型 */
	private AwardType awardType;
	/** 对应的奖励积分 */
	private BigDecimal point;
	private Gender gender;
	private String wesKits;

	/** 已解析到的配置对象 */
	protected transient RuleConfig resolvedCfg;
	protected transient FactorConfig resolvedFactorCfg;
	protected transient I18nContent resolvedLanguageCfg;

	public TaskRule(User user, String name, TaskRuleType ruleType, Integer minCoin, Integer maxCoin, Date beginTime, Date endTime, String remark) {
		this.userId = Bean.toLongId(user);
		this.name = name;
		this.ruleType = ruleType;
		this.minCoin = minCoin;
		this.maxCoin = maxCoin;
		this.beginTime = beginTime;
		this.endTime = endTime;
		this.remark = StringUtil.toString(remark);
	}

	public TaskRule() {
	}

	public TaskRule init(TaskRule input, Date now) {
		this.userId = X.expectNotNull(userId, input.userId);
		this.icon = X.expectNotEmpty(input.icon, icon);
		this.name = X.expectNotNull(input.name, name);
		this.type = X.expectNotNull(input.type, type);
		this.ruleType = X.expectNotNull(input.ruleType, ruleType);
		this.minCoin = input.minCoin;
		this.maxCoin = input.maxCoin;
		this.maxLimit = X.expectNotNull(input.maxLimit, maxLimit, 1);
		this.beginTime = X.expectNotNull(input.beginTime, beginTime);
		this.endTime = X.expectNotNull(input.endTime, endTime);
		this.remark = StringUtil.toString(input.remark);
		this.sort = X.expectNotNull(input.sort, sort, 0);
		this.config = StringUtil.toString(input.config);
		this.factorConfig = StringUtil.toString(input.factorConfig);
		this.wesKits = input.getWesKits();
		super.initTime(now);
		super.init(input.status, input.state);
		return this;
	}

	@Override
	public void setId(Long id) {
		this.id = id;
	}

	public boolean isRandom() {
		return getResolvedFactorCfg() != null;
	}

	public String updateLabel(Integer changeCoin) {
		return I18N.msg(ruleType.getDesc(), changeCoin);
	}

	@Nullable
	public <T extends RuleConfig> T getResolvedCfg() {
		RuleConfig cfg = resolvedCfg;
		if (cfg == null) {
			resolvedCfg = cfg = ruleType.deserialize(config);
		}
		return X.castType(cfg);
	}

	public void updateCfg(RuleConfig ruleConfig) {
		this.config = ruleType.serialize(this.resolvedCfg = ruleConfig);
	}

	public <T extends RuleConfig> T updateResolvedCfg(String config) {
		this.resolvedCfg = ruleType.deserialize(this.config = config);
		return getResolvedCfg();
	}

	@Nullable
	public FactorConfig getResolvedFactorCfg() {
		FactorConfig cfg = resolvedFactorCfg;
		if (cfg == null) {
			resolvedFactorCfg = cfg = StringUtil.isEmpty(factorConfig) ? null : JSONUtil.parseObject(factorConfig, FactorConfig.class);
		}
		return X.castType(cfg);
	}

	public void updateFactorCfg(FactorConfig config) {
		this.factorConfig = JSONUtil.serialize(this.resolvedFactorCfg = config);
	}

	public FactorConfig updateResolvedFactorCfg(String config) {
		this.resolvedFactorCfg = JSONUtil.deserialize(this.factorConfig = config);
		return getResolvedFactorCfg();
	}

	public String getI18nName() {
		return X.map(getResolvedLanguageCfg(), I18nContent::get);
	}

	public String getI18nName(Language language) {
		return X.map(getResolvedLanguageCfg(), l -> l.get(language));
	}

	public String getTypeDesc() {
		return type.getDesc();
	}

	@Nullable
	public I18nContent getResolvedLanguageCfg() {
		I18nContent cfg = resolvedLanguageCfg;
		if (cfg == null) {
			resolvedLanguageCfg = cfg = StringUtil.isEmpty(name) ? null : I18nContent.deserialize(name);
		}
		return X.castType(cfg);
	}

	public void updateName(I18nContent config) {
		this.name = I18nContent.serialize(this.resolvedLanguageCfg = config);
	}

	public I18nContent updateResolvedLanguageCfg(String config) {
		this.resolvedLanguageCfg = I18nContent.deserialize(this.name = config);
		return getResolvedLanguageCfg();
	}

	@Override
	public boolean isEnabled() {
		return this.status > 0;
	}

	public boolean isActivity(Long nowTime) {
		if (nowTime == null) {
			nowTime = System.currentTimeMillis();
		}
		return isActive(nowTime);
	}

	/**
	 * 积分规则枚举
	 */
	@Getter
	public enum TaskRuleType implements LabelI18nProxy<TaskRuleType, Integer> {
		/** 签到 */
		SIGN_IN(TASK_RULE_TYPE_SIGN_IN, TASK_RULE_TYPE_SIGN_IN_DESC, "sign", false, TaskRuleTypeCore.ADD_USE, SignRuleConfig.class, true, Gender.MALE),
		/** 资料完善 */
		USER_INFO_SUPPLY(TASK_RULE_TYPE_USER_INFO_SUPPLY, TASK_RULE_TYPE_USER_INFO_SUPPLY_DESC, "completeMaterial", false, TaskRuleTypeCore.ADD_USE, InfoSupplyConfig.class, true, null),
		/** 头像认证 */
		AVATAR_AUTH(TASK_RULE_TYPE_AVATAR_AUTH, TASK_RULE_TYPE_AVATAR_AUTH_DESC, "avatarAuth", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 私信聊天 */
		IM_CHAT(TASK_RULE_TYPE_IM_CHAT, TASK_RULE_TYPE_IM_CHAT_DESC, "privateMessageChat", false, TaskRuleTypeCore.ADD_USE, true, Gender.MALE),
		/** 发布动态 */
		MOMENT_PUSH(TASK_RULE_TYPE_MOMENT_PUSH, TASK_RULE_TYPE_MOMENT_PUSH_DESC, "releaseMoments", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 发起搭讪 */
		GREET(TASK_RULE_TYPE_GREET, TASK_RULE_TYPE_GREET_DESC, "initiateSayHi", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 增加用户积分 */
		ADD_USER_COIN(TASK_RULE_TYPE_ADD_USER_COIN, TASK_RULE_TYPE_ADD_USER_COIN_DESC, null, true, TaskRuleTypeCore.ADD_USE, false, null),
		/** 扣除用户积分 */
		DEDUCT_USER_COIN(TASK_RULE_TYPE_DEDUCT_USER_COIN, TASK_RULE_TYPE_DEDUCT_USER_COIN_DESC, null, true, TaskRuleTypeCore.DEDUCT_USE, false, null),
		/** 转入钱包 */
		EXTRACT_ACCOUNT(TASK_RULE_TYPE_EXTRACT_ACCOUNT, TASK_RULE_TYPE_EXTRACT_ACCOUNT_DESC, null, true, TaskRuleTypeCore.DEDUCT_USE, false, null),
		/** 绑定手机 */
		BIND_PHONE(TASK_RULE_TYPE_BIND_PHONE, TASK_RULE_TYPE_BIND_PHONE_DESC, "bindPhone", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 动态评论 */
		MOMENT_COMMENT(TASK_RULE_TYPE_MOMENT_COMMENT, TASK_RULE_TYPE_MOMENT_COMMENT_DESC, "momentComment", false, TaskRuleTypeCore.ADD_USE, false, null),
		/** 图片消息 */
		IM_CHAT_PICTURE(TASK_RULE_TYPE_REAL_IM_CHAT_PICTURE, TASK_RULE_TYPE_REAL_IM_CHAT_PICTURE_DESC, "home_chat_list", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 语音消息 */
		IM_CHAT_AUDIO(TASK_RULE_TYPE_IM_CHAT_AUDIO, TASK_RULE_TYPE_IM_CHAT_AUDIO_DESC, "home_chat_list", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 发起动态广场搭讪 */
		MOMENT_GREET(TASK_RULE_TYPE_MOMENT_GREET, TASK_RULE_TYPE_MOMENT_GREET_DESC, "momentGreet", false, TaskRuleTypeCore.ADD_USE, false, Gender.MALE),
		/** 亲密度等级 */
		INTIMACY_LEVEL(TASK_RULE_TYPE_INTIMACY_LEVEL, TASK_RULE_TYPE_INTIMACY_LEVEL_DESC, "home_chat_list", false, TaskRuleTypeCore.ADD_USE, IntimacyLevelConfig.class, true, null),
		/** 语音通话 */
		IM_AUDIO(TASK_RULE_TYPE_IM_AUDIO, TASK_RULE_TYPE_IM_AUDIO_DESC, "home_chat_list", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 视频通话 */
		IM_VIDEO(TASK_RULE_TYPE_IM_VIDEO, TASK_RULE_TYPE_IM_VIDEO_DESC, "home_chat_list", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 添加相册图片 */
		ADD_PHOTO_IMG(TASK_RULE_TYPE_PHOTO_IMG, TASK_RULE_TYPE_PHOTO_IMG_DESC, "completeMaterial", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 添加相册视频 */
		ADD_PHOTO_VIDEO(TASK_RULE_TYPE_PHOTO_VIDEO, TASK_RULE_TYPE_PHOTO_VIDEO_DESC, "completeMaterial", false, TaskRuleTypeCore.ADD_USE, true, null),
		/** 上传头像 */
		ADD_USER_AVATAR(TASK_RULE_TYPE_USER_AVATAR, TASK_RULE_TYPE_USER_AVATAR_DESC, "completeMaterial", false, TaskRuleTypeCore.ADD_USE, true, null),
		//
		;
		/** 规则枚举描述 */
		public String desc;
		public String url;
		/** 是否显示 */
		public boolean visible;
		/** 内部设置 */
		public boolean innerSet;
		public TaskRuleTypeCore from;
		public final Class<? extends RuleConfig> configClazz;
		final ValueProxy<TaskRuleType, Integer> proxy;
		public final Gender gender;
		public WesKit wesKit;
		//

		TaskRuleType(String label, String desc, String url, boolean innerSet, TaskRuleTypeCore from, Class<? extends RuleConfig> configClazz, boolean visible, Gender gender) {
			this.desc = desc;
			this.url = url;
			this.visible = visible;
			this.innerSet = innerSet;
			this.from = from;
			this.configClazz = configClazz;
			this.proxy = new ValueProxy<>(this, ordinal(), label);
			this.gender = gender;
		}

		TaskRuleType(String label, String desc, String url, boolean innerSet, TaskRuleTypeCore from, boolean visible, Gender gender) {
			this(label, desc, url, innerSet, from, null, visible, gender);
		}

		public TaskAccountLog trade(final TaskAccount account, TaskAccountLog targetLog, final int changeCoin) {
			final TaskAccountLog log = from.trade(account, targetLog, changeCoin);
			log.setFlow(from.getFlow());
			log.setRuleType(this);
			return log;
		}

		public String serialize(RuleConfig config) {
			return config == null ? "" : JSON.toJSONString(config);
		}

		public RuleConfig deserialize(String configJSONStr) {
			return StringUtil.isEmpty(configJSONStr) ? null : JSONUtil.parseObject(configJSONStr, configClazz);
		}

		public Integer getFactor() {
			return from.getFlow();
		}

		public static TaskRuleType[] getValues() {
			return ValueProxy.getCachedArray(TaskRuleType.class, TaskRuleType::values);
		}

		public static TaskRuleType of(Integer value) {
			return SIGN_IN.getValueOf(value);
		}
	}

	public enum TaskRuleTypeCore {
		// 总金额不变
		/**
		 * 解冻钻石(冻结转可用): 总钻石不变，可用增加,冻结减少
		 */
		FROZEN_TO_USE("解冻钻石", "0+-"),
		/**
		 * 冻结钻石:总钻石不变，可用减少,冻结增加
		 */
		FROZEN("冻结钻石", "0-+"),
		/**
		 * 增加可用: 总钻石增加，可用增加,冻结不变
		 */
		ADD_USE("增加可用", "++0"),
		// 总金额减少
		/**
		 * 扣除冻结钻石: 总钻石减少，可用不变,冻结减少
		 */
		DEDUCT_FROZEN("扣除冻结钻石", "-0-"),
		/**
		 * 扣除可用: 总钻石减少,可用减少,冻结不变
		 */
		DEDUCT_USE("扣除可用", "--0");

		private final String label;
		public final char[] operators;
		private final Integer flow;

		TaskRuleTypeCore(String label, String operators) {
			this.operators = operators.toCharArray();
			if (this.operators.length != 3) {
				throw new IllegalArgumentException("The expected length of operators is 3, but actually is " + this.operators.length);
			}
			this.label = label;
			this.flow = calcFlow(this.operators);
		}

		public String getLabel() {
			return label;
		}

		public TaskAccountLog trade(TaskAccount account, TaskAccountLog log, int total) {
			return X.expectNotNull(log, new TaskAccountLog()).init(account, total, operators[0], operators[1], operators[2]);
		}

		/**
		 * 返回该交易类型的页面展示符号
		 *
		 * @return '+' 或 '0' 或 '-'
		 */
		public char getSign() {
			return operators[1];
		}

		public Integer getFlow() {
			return flow;
		}

		private static int calcFlow(final char[] operators) {
			final char a = operators[1], f = operators[2];
			int aInt = a == '+' ? 1 : (a == '-' ? -1 : 0), fInt = f == '+' ? 1 : (f == '-' ? -1 : 0);
			int result = aInt + fInt;
			if (result > 0) {
				return 1;
			} else if (result < 0) {
				result = -1;
			}
			return result;
		}
	}

	/**
	 * 奖励类型
	 */
	public static enum AwardType {
		COIN("钻石"),
		POINT("积分"),
		;
		private final String label;

		AwardType(String label) {
			this.label = label;
		}

	}

	public static final String USER_ID = "user_id";
	public static final String ICON = "icon";
	public static final String NAME = "name";
	public static final String TYPE = "type";
	public static final String RULE_TYPE = "rule_type";
	public static final String MIN_COIN = "min_coin";
	public static final String MAX_COIN = "max_coin";
	public static final String MAX_LIMIT = "max_limit";
	public static final String BEGIN_TIME = "begin_time";
	public static final String END_TIME = "end_time";
	public static final String CONFIG = "config";
	public static final String FACTOR_CONFIG = "factor_config";
	public static final String REMARK = "remark";
	public static final String SORT = "sort";

}
